package de.uni_bremen.exceptions.oops;

import java.util.LinkedList;

/**
 * Die Klasse repräsentiert die Anweisung IF-THEN im Syntaxbaum.
 */
class IfStatement extends Statement
{
	protected String getRef()
	{
		return "IF";
	}
	
	/** Die Bedingung der IF-Anweisung. */
	Expression				condition;
	
	/** Die Anweisungen im THEN-Teil. */
	LinkedList<Statement>	thenStatements	= new LinkedList<Statement>();
	
	/** Die If-Anweisung im ELSE-IF-Teil. */
	ElseIfStatement			elseIfStatement;
	
	/** Die Anweisungen im ELSE-Teil. */
	LinkedList<Statement>	elseStatements	= new LinkedList<Statement>();
	
	/**
	 * Konstruktor.
	 * 
	 * @param condition
	 *            Die Bedingung der IF-Anweisung.
	 */
	IfStatement(Expression condition)
	{
		this.condition = condition;
	}
	
	/**
	 * Prüft, ob auf allen Pfaden ein <b>RETURN</b> errreicht wird.
	 */
	boolean returns()
	{
		boolean returns = false;
		for (Statement s : thenStatements)
			if (s.returns())
				returns = true;
		if (!returns)
			return false;
		if (elseIfStatement != null)
			returns = elseIfStatement.returns();
		if (!returns)
			return false;
		returns = false;
		for (Statement s : elseStatements)
			if (s.returns())
				return true;
		return false;
	}
	
	void evaluateExpressions()
	{
		if (condition instanceof IEvaluable)
			condition = ((IEvaluable) condition).eval();
		for (Statement s : thenStatements)
			s.evaluateExpressions();
		if (elseIfStatement != null)
			elseIfStatement.evaluateExpressions();
		for (Statement s : elseStatements)
			s.evaluateExpressions();
	}
	
	/**
	 * Die Methode führt die Kontextanalyse für diese Anweisung durch.
	 * 
	 * @param declarations
	 *            Die an dieser Stelle gültigen Deklarationen.
	 * @throws CompileException
	 *             Während der Kontextanylyse wurde ein Fehler gefunden.
	 */
	void contextAnalysis(Declarations declarations) throws CompileException
	{
		condition = condition.contextAnalysis(declarations);
		condition = condition.unBox();
		condition.type.check(ClassDeclaration.boolType, condition.position);
		for (Statement s : thenStatements)
		{
			s.contextAnalysis(declarations);
		}
		if (elseIfStatement != null)
			elseIfStatement.contextAnalysis(declarations);
		for (Statement s : elseStatements)
		{
			s.contextAnalysis(declarations);
		}
	}
	
	/**
	 * Die Methode gibt diese Anweisung in einer Baumstruktur aus.
	 * 
	 * @param tree
	 *            Der Strom, in den die Ausgabe erfolgt.
	 */
	void print(TreeStream tree)
	{
		tree.println(getRef());
		tree.indent();
		condition.print(tree);
		if (!thenStatements.isEmpty())
		{
			tree.println("THEN");
			tree.indent();
			for (Statement s : thenStatements)
				s.print(tree);
			tree.unindent();
			if (elseIfStatement != null)
				elseIfStatement.print(tree);
			if (elseStatements.size() > 0)
			{
				tree.println("ELSE");
				tree.indent();
				for (Statement s : elseStatements)
					s.print(tree);
				tree.unindent();
			}
		}
		tree.unindent();
	}
	
	/**
	 * Die Methode generiert den Assembler-Code für diese Anweisung. Sie geht davon aus, dass die Kontextanalyse vorher
	 * erfolgreich abgeschlossen wurde.
	 * 
	 * @param code
	 *            Der Strom, in den die Ausgabe erfolgt.
	 */
	void generateCode(CodeStream code)
	{
		String endLabel = code.nextLabel();
		generateCode(code, endLabel);
	}
	
	/**
	 * Die Methode generiert den Assembler-Code für diese Anweisung. Sie geht davon aus, dass die Kontextanalyse vorher
	 * erfolgreich abgeschlossen wurde.
	 * 
	 * @param code
	 *            Der Strom, in den die Ausgabe erfolgt.
	 * @param endLabel
	 *            Label, mit der hinter die IfStatements bzw ElseStatements gesrungen werden kann.
	 */
	protected void generateCode(CodeStream code, String endLabel)
	{
		String elseLabel = null;
		if (hasElse())
			elseLabel = code.nextLabel();
		code.println("; " + getRef());
		condition.generateCode(code);
		code.println("MRM R5, (R2) ; Bedingung vom Stapel nehmen");
		code.println("SUB R2, R1");
		code.println("ISZ R5, R5 ; Wenn 0, dann");
		if (hasElse())
			code.println("JPC R5, " + elseLabel + " ; Sprung zu ELSE");
		else
			code.println("JPC R5, " + endLabel + " ; Sprung zu END IF");
		code.println("; THEN");
		for (Statement s : thenStatements)
			s.generateCode(code);
		code.println("JPC R5, " + endLabel + " ; Sprung zu END IF");
		if (elseIfStatement != null)
		{
			code.println(elseLabel + ":");
			elseIfStatement.generateCode(code, endLabel);
		}
		if (elseStatements.size() > 0)
		{
			code.println("; ELSE");
			code.println(elseLabel + ":");
			for (Statement s : elseStatements)
				s.generateCode(code);
		}
		if (getRef().equals("IF"))
		{
			code.println("; END IF");
			code.println(endLabel + ":");
		}
	}
	
	/**
	 * Die Methode überprüft, ob diese Anweisung ELSEIF oder mindestens einen ELSE-Zweig enthält.
	 * 
	 * @return Gibt true aus, wenn ELSE oder ELSEIF vorhanden ist.
	 */
	private boolean hasElse()
	{
		return elseIfStatement != null || elseStatements.size() > 0;
	}
	
	/**
	 * Die Methode gibt diese Anweisung in Textform aus.
	 */
	@Override
	public String toString()
	{
		return "IF " + condition;
	}
}
